
;*******************************************************
;
;	SCSI Driver 'Shutdown' filter.
;
;	Written by Matt Gulick.		Started August 9,1988
;
;	Copyright Apple Computer, Inc. 1988-90
;
;*******************************************************

;*******************************************************
;
;	This file contains the 'Shutdown' filter as defined
;	in the ERS.
;
;*******************************************************

;*******************************************************
;
;	Revision History:
;
;*******************************************************
;
;	Aug 9,		1988	File started.
;	Mar	8,		1989	Modified to support Warm/Cold
;						Shutdown.

				STRING		PASCAL
				BLANKS		OFF
				PAGESIZE	70
				PRINT		NOGEN
				PRINT		NOMDIR
				MACHINE		M65816

				IMPORT		direct_page
				IMPORT		default_dib
				IMPORT		tot_dib_cnt
				IMPORT		active_starts
				IMPORT		stop_unit

				PRINT		OFF

				INCLUDE		'scsihd.equates'
				INCLUDE		'M16.MEMORY'
				INCLUDE		'M16.UTIL'
				PRINT		ON

				EJECT
			
;*******************************************************
;
;	Main Entry point to the 'Shutdown' filter.  This
;	"Filter" is called when a Device is no longer
;	desired in the tables.  The following code segment
;	first checks to see if the DIB being shut down is
;	the default dib (it will only be shutdown durring
;	startup).  If it is the default dib we will do
;	nothing and exit with no error.  If on the other
;	hand it is a valid DIB, we will clear some key
;	fields within the dib and then we will alter the
;	links to skip this DIB, then we update a field in the
;	first DIB of this memory segment.  This count tells
;	how many active dibs there are in that segment.  If
;	this reaches zero we will then dispose this handle
;	because it is unused.  If we later need space to
;	bring new devices online and we don't have the space,
;	we'll get it at that time.  If on exit there are
;	still any active dibs that use this code segment then
;	a DRVR_BUSY error will be returned.  If no DIBs are
;	active then no error will be returned.
;
;	Inputs:		[dib_ptr]	=	Last DIB built		(LONG)
;				Acc			=	Unspecified
;				Carry		=	Unspecified
;				Y register	=	Unspecified
;				X register	=	Unspecified
;				P register	=	0=M=X=e
;				Direct Page	=	Ours
;				Data Bank	=	Ours
;
;	Outputs:	Acc			=	DRVR_BUSY
;				Carry		=	1
;						unless we have no more dibs then
;				Acc			=	0
;				Carry		=	0
;
;				Y register	=	Unspecified
;				X register	=	Unspecified
;				P register	=	0=M=X=e
;				Direct Page	=	GS/OS Direct Page
;				Data Bank	=	Ours
;
;	Errors:		See Spec.
;
;*******************************************************

				EXPORT	shutdown
shutdown		PROC
												;
												; Check to see if this is for the
												; 'default_dib'.  If it is, then we
												; need to exit with a DRVR_BUSY Error.
												;
				lda		<dib_ptr
				cmp		#default_dib
				bne		@real_dib
				lda		<dib_ptr+2
				cmp		#^default_dib
				bne		@real_dib
												;
												; Return a DRVR BUSY error and take
												; no action.
												;
				lda		#drvr_busy
				sec
				rts

@real_dib

;-------------------------------------------------------------------------------

				IF		warm_ss_suprt = true		THEN

												;
												; It's a real DIB.  Is it a COLD
												; SHUTDOWN?
												;
				lda		>warm_cold_flag
				and		#$0001
				beq		@do_cold				;It's Cold
												;
												; Yeah, so it's a warm shutdown.  But
												; does this device know what that
												; means?  We'll see!
												;
				ldy		#dib.dvcchar
				lda		[dib_ptr],y
				and		#restartable
				beq		@do_cold				;Not restartable.
												;
												; Mark as OFFLINE and Relaxing
												;
				ldy		#dib.dvcflag
				lda		[dib_ptr],y
				and		#dvc_online--\
						$ffff
				ora		#relaxing
				sta		[dib_ptr],y
												;
												; Reinitialise DIB Device Number to
												; zero.
												;
				lda		#null
				ldy		#dib.devnum
				sta		[dib_ptr],y
												;
												; Do the Head and Forward links also.
												;
				ldy		#dib.headlnk
				sta		[dib_ptr],y

				ldy		#dib.fdvclnk
				sta		[dib_ptr],y
												;
												; Get out of here updating only
												; the active DIB count not the
												; total DIB Count.
												;
@continue		jmp		@update_acnt

 				ENDIF

;-------------------------------------------------------------------------------

												;
												; It's a real DIB.  Issue a $1B
												; START/STOP Command.  Ignor all
												; errors, not all devices support
												; this optional command.
												;
@do_cold		jsr		stop_unit
												;
												; Set Device to Default.
												;
				ldy		#dib.dvcflag
				lda		#wait_mode++\
						cold_dib
				sta		[dib_ptr],y
												;
												; Shut it down.  To do this we clear
												; the DIB Device Number.
												;
				ldy		#dib.devnum
				lda		[dib_ptr],y
				sta		@this_device			;Save for a while
				lda		#null
				sta		[dib_ptr],y
												;
												; Dec Device count for this memory
												; segment.
												;
				ldy		#dib.handle				;Set up to deref the handle to find
				lda		[dib_ptr],y				;where this memory segment starts.
				sta		<scsi_zp0
				sta		|@deref_handle			;Save in case we dispose of it.
				ldy		#dib.handle+2
				lda		[dib_ptr],y
				sta		<scsi_zp0+2
				sta		|@deref_handle+2
												;
												; Deref the Handle.
												;
				ldy		#$0002
				lda		[scsi_zp0]
				tax
				lda		[scsi_zp0],y
				sta		scsi_zp0+2
				stx		scsi_zp0
												;
												; Dec the count.  If now = zero then
												; dispose of this handle.
												;
				ldy		#dib.mem_dib_cnt
				lda		[scsi_zp0],y
				dec		a
				sta		[scsi_zp0],y

				bne		@chk_links				;Not zero.  Update link if it exists.
												;
												; We have emptied this memory segment.
												; We now need to deallocate this
												; memory segment.
												;
				ldy		#$0002
				pushword |@deref_handle+2
				pushword |@deref_handle
				_disposehandle					
												;
												; Find location of previous DIB in
												; linked list.
												;
												; Start at the default DIB.
												;
				lda		#default_dib
				sta		<prev_dib
				lda		#^default_dib
				sta		<prev_dib+2
												;
												; Does this DIB point to the
												; requseted DIB?
												;
				ldy		#dib.linkptr+2

@prev_loop		lda		[prev_dib],y
				tax								;Save value for later

				lda		[prev_dib]				;Do they match?
				cmp		<dib_ptr
				bne		@advnc_loop				;No.
				cpx		<dib_ptr+2
				beq		@set_prev				;Yes. Set new links.

@advnc_loop		stx		<prev_dib+2				;No.  Advance to the next DIB.
				sta		<prev_dib
				ora		<prev_dib+2
				bne		@prev_loop
				bra		@chk_links
												;
												; Set previous DIBs link pointer
												; to the DIB that follws the one
												; being shutdown.
												;
@set_prev		lda		[dib_ptr]
				sta		[prev_dib]
				lda		[dib_ptr],y
				sta		[prev_dib],y
												;
												; We must now check to see if this DIB
												; that was shutdown is linked to any
												; other DIBs.
												;
@chk_links		ldy		#dib.dvcchar
				lda		[dib_ptr],y
				and		#linked_dvc
				beq		@update_tcnt			;No it is not linked.
												;
												; It is linked. First we need to go to
												; the head device if this is not it. If
												; it is the head, then we need to do
												; something completly different.
												;
				clc
				ldy		#dib.headptr
				lda		[dib_ptr],y
				ldy		#dib.headptr+2
				ora		[dib_ptr],y
				beq		@at_head

				lda		[dib_ptr],y
				sta		<scsi_zp0+2
				ldy		#dib.headptr
				lda		[dib_ptr],y
				sta		<scsi_zp0
				bra		@chk_lnk_loop

@at_head		lda		<dib_ptr
				sta		<scsi_zp0
				lda		<dib_ptr+2
				sta		<scsi_zp0+2
				
@chk_lnk_loop	ldy		#dib.fdvclnk
				lda		[scsi_zp0],y
				beq		@update_tcnt

				cmp		@this_device
				beq		@update_link

				ldy		#dib.fdvcptr
				lda		[scsi_zp0],y
				tax
				ldy		#dib.fdvcptr+2
				lda		[scsi_zp0],y
				sta		<scsi_zp0+2
				stx		<scsi_zp0
				bra		@chk_lnk_loop

@update_link	ldy		#dib.headptr
				lda		[dib_ptr],y
				sta		[scsi_zp0],y
				ldy		#dib.headptr+2
				lda		[dib_ptr],y
				sta		[scsi_zp0],y

				ldy		#dib.fdvcptr
				lda		[dib_ptr],y
				sta		[scsi_zp0],y
				ldy		#dib.fdvcptr+2
				lda		[dib_ptr],y
				sta		[scsi_zp0],y

				ldy		#dib.fdvclnk
				lda		[dib_ptr],y
				sta		[scsi_zp0],y
												;
												; Clear the FDVCLNK and HEADLNKs
												; incase this segment is used later.
												;
				lda		#null
				sta		[dib_ptr],y
				ldy		#dib.headlnk
				sta		[dib_ptr],y
												;
												; Update the total DIB Count.  If
												; there are still some dibs left,
												; then we will return a DRVR_BUSY
												; error.  If all done we will return
												; no error.  This will cause us to
												; be purged.
												;
@update_tcnt	dec		|tot_dib_cnt
@update_acnt	dec		|active_starts
				beq		@thats_all_folks
												;
												; More left. Return an error.
												;
				lda		#DRVR_BUSY
				sec
				rts
												;
												; ThThThThThaaaaattt'ss all folks.
												;
@thats_all_folks
												;
												; Exit no Error.
												;
				lda		#null
				clc
				rts
												;
												; Internal Data Areas.
												;
@deref_handle	dc.l	null
@this_device	dc.w	null

				ENDP

				END

				EJECT
